ShowTable of Contents
This article provides a recommended best practice for National Language Support (NLS) when building plug-ins for Lotus Expeditor, Lotus Notes, Lotus Sametime, and Lotus Symphony.
Using Eclipse NLS framework
Description
While Java
ResourceBundles
have been the standard mechanism for externalizing and loading translated Strings for some time, they display inefficient space characteristics. Since a running Eclipse tends to have many externalized messages we have begun to use a new message bundle story in Expeditor code and recommend customers to the same. The mechanism is quite simple and completely generic - it can be used anywhere.
Summary of the Eclipse NLS approach
- messages.properties - this file is same as before except all keys need to be valid Java identifiers.
- Each message file has a corresponding Java class.
- Each key/value pair in the file has a public static String field whose name is the same as the message key.
- When message bundles are loaded, the values of the fields are set to be the values from the messages.properties files.
- The message properties files are purged from memory.
When creating a new message
- create a field in your Messages.java file
- create a key/value pair in your messages.properties file where the key name matches the field name
- To reference the message, simply reference the field (e.g. Messages.my_key) rather than the standard lookup
Source code examples
This example consists of three files.
- MyClass.java - An example application class that needs to load and use Strings for the appropriate Locale. Many classes in your application may need to be doing this.
- Messages.java - The class responsible for storing the messages in memory and making them available to the rest of the application code. You will generally have only one class per bundle that loads all of the messages.
- message.properties - This is a properties file that contains your externalized Strings. The default language (usually US English) is stored in this file. Translated messages are stored in a file named message__ where is the 2 letter language code and is the 2 lette geography code. Example include de_DE for German in Germany, pt_BR for Portuguese in Brazil, etc.
Application class called MyClass.java
Old format (ResourceBundle based)
public class MyClass {
public void myMethod() {
String message;
...
// no args
message = Messages.getString("key.one"); //$NON-NLS-1$
...
// bind one arg
message = MessageFormat.format(Messages.getString("key.two"), new Object[] {"example usage"}); //$NON-NLS-1$ //$NON-NLS-2$
...
}
}
New recommended format
public class MyClass {
public void myMethod() {
String message;
...
// no args
message = Messages.key_one;
...
// bind one arg
message = NLS.bind(Messages.key_two, "example usage"); //$NON-NLS-1$
...
}
}
Messages.java
Old format (ResourceBundle based)
public class Messages {
private static final String BUNDLE_NAME = "com.company.app.bundleA"; //$NON-NLS-1$
private static final ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_NAME);
public static String getString(String key) {
try {
return bundle.getString(key);
} catch (MissingResourceException e) {
return key;
}
}
}
New recommended format
import org.eclipse.osgi.util.NLS;
public class Messages extends NLS {
private static final String BUNDLE_NAME = "com.company.app.bundleA"; //$NON-NLS-1$
public static String key_one;
public static String key_two;
...
static {
NLS.initializeMessages(BUNDLE_NAME, Messages.class);
}
}
In this example, the NLS.initializeMessages(BUNDLE_NAME, Messages.class); results in the appropriate locale messages getting loaded.
messages.properties
Old format (ResourceBundle based)
key.one = Hello world.
key.two = This is an {0} of binding with one argument.
New recommended format
key_one = Hello world.
key_two = This is an {0} of binding with one argument.
Benefits
One of the driving forces behind the introduction of this framework into Eclipse was to improve performance. The following section provides some details on the improvements provided.
Time
- Using a message is marginally faster since it is just a field access rather than a lookup in the resource bundle.
- Time to load and initialize a bundle is VM dependant but we have seen 5% to 46% improvements.
Memory Footprint
- This is very much a scalability play. The more you use, the more you save.
- The rough space savings is 88 + 4N bytes per message where N is the number of characters in the key.
- The absolute best-case scenerio for the Eclipse SDK, if every property file is loaded and every key referenced, is roughly 4.5M of memory. (based on the January 11, 2005 integration build)
- Realistic scenarios for the Eclipse IDE should see savings on the order of 500KB
Other benefits
- Easily catch missing keys - Message lookups are now field accesses so you cannot reference a key that doesn't exist or you will get a compile error.
- Easily find typos in code when referencing keys - Each key is represented by a field in the class so if the referencing code makes a spelling error, then you will get a compile error.
- Find unused keys - During the development cycle code is deleted, moved and messages are changed. As a result there are keys in the messages.properties file which are never referenced.These can easily be found now since you can just do a search for references to the field. If there are no references, then delete the field from the class and the key/value pair from the file.
Drawbacks
- There are now 2 files to maintain - Now the messages.properties and the java file must be kept in sync. There is an opportunity for tooling to help with this; a validation tool could indicate problems with markers. Currently there are debug options which log entries which exsit in one file but not the other.
Locating "broken" strings
Eclipse provides tooling to assist with the maintenance of the java and properties files. The tooling works for both the NLS model, and the ResourceBundle model. To use the tooling, select a project, then select Source > Find Broken Externalized Strings. This performs a search to look for missing keys, or keys that are included that are not referenced.
There are some specific requirements to using the tools:
- For NLS model, the NLS subclass must include a static called BUNDLE_NAME that refers to the properties file name.
- The tools expect to locate default .properties files, not _en.properties files as currently exist in the projects. You can copy the _en.properties to a default .properties in the current projects (but don't add to source control), and then run the tool.
- For ResourceBundle style usage, wrapper classes that refer to a properties file name should include the field BUNDLE_NAME
If you see a dialog with the message No NLS property files with corresponding accessor class found in selection, it means that the classes and the properties files are not appropriately referenced. Check NLS style subclasses and the wrapper classes to be sure that they contain the BUNDLE_NAME field.